home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2001 November / november_2001.iso / Browsers / Netscape 6.1 / browser.xpi / bin / chrome / toolkit.jar / content / global / nsTreeController.js < prev    next >
Encoding:
Text File  |  2001-07-19  |  13.7 KB  |  444 lines

  1. /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public
  4.  * License Version 1.1 (the "License"); you may not use this file
  5.  * except in compliance with the License. You may obtain a copy of
  6.  * the License at http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the License is distributed on an "AS
  9.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  10.  * implied. See the License for the specific language governing
  11.  * rights and limitations under the License.
  12.  *
  13.  * The Original Code is mozilla.org code.
  14.  *
  15.  * The Initial Developer of the Original Code is Netscape
  16.  * Communications Corporation.  Portions created by Netscape are
  17.  * Copyright (C) 1998 Netscape Communications Corporation. All
  18.  * Rights Reserved.
  19.  *
  20.  * Contributor(s): 
  21.  *   Peter Annema <disttsc@bart.nl>
  22.  *   Blake Ross <blakeross@telocity.com>
  23.  *   Alec Flett <alecf@netscape.com>
  24.  */
  25.  
  26. // helper routines, for doing rdf-based cut/copy/paste/etc
  27.  
  28. const NC_NS  = "http://home.netscape.com/NC-rdf#";
  29. const RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
  30. const nsTransferable_contractid = "@mozilla.org/widget/transferable;1";
  31. const clipboard_contractid = "@mozilla.org/widget/clipboard;1";
  32. const rdf_contractid = "@mozilla.org/rdf/rdf-service;1";
  33. const separatorUri = NC_NS + "BookmarkSeparator";
  34. const supportswstring_contractid = "@mozilla.org/supports-wstring;1";
  35. const rdfc_contractid = "@mozilla.org/rdf/container;1";
  36.  
  37. // some oft-used interfaces
  38. const nsISupportsWString = Components.interfaces.nsISupportsWString;
  39. const nsIClipboard = Components.interfaces.nsIClipboard;
  40. const nsITransferable = Components.interfaces.nsITransferable;
  41. const nsIRDFLiteral = Components.interfaces.nsIRDFLiteral;
  42. const nsIRDFContainer = Components.interfaces.nsIRDFContainer;
  43.  
  44. var Clipboard = Components.classes[clipboard_contractid].getService(Components.interfaces.nsIClipboard);
  45. var RDF = Components.classes[rdf_contractid].getService(Components.interfaces.nsIRDFService);
  46.  
  47. var nameResource = RDF.GetResource(NC_NS + "Name");
  48. var typeRes = RDF.GetResource(RDF_NS + "type");
  49. var bmTypeRes = RDF.GetResource(NC_NS + "Bookmark");
  50. // this is a hack for now - just assume containment
  51. var containment = RDF.GetResource(NC_NS + "child");
  52.  
  53. function isContainer(node)
  54. {
  55.     return node.getAttribute("container") == "true";
  56. }
  57.  
  58. function getWStringData(wstring, len)
  59. {
  60.     wstring = wstring.QueryInterface(nsISupportsWString);
  61.     var result = wstring.data.substring(0, len/2);
  62.     return result;
  63. }
  64.  
  65. function nsTreeController_SetTransferData(transferable, flavor, text)
  66. {
  67.     if (!text) return;
  68.     var textData = Components.classes[supportswstring_contractid].createInstance(nsISupportsWString);
  69.     textData.data = text;
  70.  
  71.     transferable.addDataFlavor(flavor);
  72.     transferable.setTransferData(flavor, textData, text.length*2);
  73. }
  74.  
  75. function nsTreeController_copy(tree)
  76. {
  77.     var select_list = tree.selectedItems;
  78.     if (!select_list) return false;
  79.     if (select_list.length < 1) return false;
  80.     
  81.     var datasource = tree.database;
  82.  
  83.     // Build a url that encodes all the select nodes 
  84.     // as well as their parent nodes
  85.     var url = "";
  86.     var text = "";
  87.     var html = "";
  88.  
  89.     for (var nodeIndex = 0; nodeIndex < select_list.length; nodeIndex++)
  90.     {
  91.         var node = select_list[nodeIndex];
  92.         if (!node) continue;
  93.  
  94.         // for now just use the .id attribute.. at some point we should support
  95.         // the #URL property for anonymous resources (see bookmarks.js)
  96.         //var ID = getAbsoluteID("bookmarksTree", node);
  97.         var ID = node.id;
  98.         var RDF = Components.classes[rdf_contractid].getService(Components.interfaces.nsIRDFService);
  99.         var IDRes = RDF.GetResource(ID);
  100.  
  101.         var nameNode = datasource.GetTarget(IDRes, nameResource, true);
  102.         nameNode =
  103.             nameNode.QueryInterface(nsIRDFLiteral);
  104.         
  105.         var theName = nameNode.Value;
  106.  
  107.         url += "ID:{" + ID + "};";
  108.         url += "NAME:{" + theName + "};";
  109.  
  110.         if (isContainer(node))
  111.         {
  112.             // gack, need to generalize this
  113.             var type = node.getAttribute("type");
  114.             if (type == seperatorUri)
  115.             {
  116.                 // Note: can't encode separators in text, just html
  117.                 html += "<hr><p>";
  118.             }
  119.             else
  120.             {
  121.                 text += ID + "\r";
  122.             
  123.                 html += "<a href='" + ID + "'>";
  124.                 if (theName) html += theName;
  125.                 html += "</a><p>";
  126.             }
  127.         }
  128.     }
  129.  
  130.     if (url == "") return false;
  131.  
  132.     // get some useful components
  133.     var trans = Components.classes[nsTransferable_contractid].createInstance(nsITransferable);
  134.  
  135.     Clipboard.emptyClipboard(nsIClipboard.kGlobalClipboard);
  136.  
  137.     this.SetTransferData(trans, "moz/bookmarkclipboarditem", url);
  138.     this.SetTransferData(trans, "text/unicode", text);
  139.     this.SetTransferData(trans, "text/html", html);
  140.  
  141.     Clipboard.setData(trans, null, nsIClipboard.kGlobalClipboard);
  142.     return true;
  143. }
  144.  
  145. function nsTreeController_cut(tree)
  146. {
  147.     if (this.copy(tree)) {
  148.         this.doDelete(tree);
  149.         return true;            // copy succeeded, don't care if delete failed
  150.     } else
  151.         return false;           // copy failed, so did cut
  152. }
  153.  
  154. function nsTreeController_paste(tree)
  155. {
  156.     var select_list = tree.selectedItems;
  157.     if (!select_list) return false;
  158.     if (select_list.length != 1) return false;
  159.     
  160.     var datasource = tree.database;
  161.     
  162.     var pasteNodeID = select_list[0].id;
  163.     var isContainerFlag = isContainer(select_list[0]);
  164.  
  165.  
  166.     var trans_uri = "@mozilla.org/widget/transferable;1";
  167.     var trans = Components.classes[nsTransferable_contractid].createInstance(nsITransferable);
  168.     trans.addDataFlavor("moz/bookmarkclipboarditem");
  169.  
  170.     Clipboard.getData(trans, nsIClipboard.kGlobalClipboard);
  171.     var data = new Object();
  172.     var dataLen = new Object();
  173.     trans.getTransferData("moz/bookmarkclipboarditem", data, dataLen);
  174.     
  175.     var url = getWStringData(data.value, dataLen.value);
  176.     if (!url) return false;
  177.  
  178.     var strings = url.split(";");
  179.     if (!strings) return false;
  180.  
  181.     var RDFC = Components.classes[rdfc_contractid].getService(nsIRDFContainer);
  182.     
  183.     var nameRes = RDF.GetResource(NC_NS + "Name");
  184.     if (!nameRes) return false;
  185.  
  186.     pasteNodeRes = RDF.GetResource(pasteNodeID);
  187.     if (!pasteNodeRes) return false;
  188.     
  189.     var pasteContainerRes = null;
  190.     var pasteNodeIndex = -1;
  191.     if (isContainerFlag == true)
  192.     {
  193.         pasteContainerRes = pasteNodeRes;
  194.     }
  195.     else
  196.     {
  197.         var parID = select_list[0].parentNode.parentNode.getAttribute("ref");
  198.         if (!parID) {
  199.             parID = select_list[0].parentNode.parentNode.getAttribute("id");
  200.         }
  201.         if (!parID) return false;
  202.         pasteContainerRes = RDF.GetResource(parID);
  203.         if (!pasteContainerRes) return false;
  204.     }
  205.     RDFC.Init(datasource, pasteContainerRes);
  206.  
  207.     if (isContainerFlag == false)
  208.     {
  209.         pasteNodeIndex = RDFC.IndexOf(pasteNodeRes);
  210.         if (pasteNodeIndex < 0) return false; // how did that happen?
  211.     }
  212.  
  213.     var dirty = false;
  214.     for (var x=0; x<strings.length; x=x+2)
  215.     {
  216.         var theID = strings[x];
  217.         var theName = strings[x+1];
  218.  
  219.         // make sure we have a real name/id combo
  220.         if ((theID.indexOf("ID:{") != 0) ||
  221.             (theName.indexOf("NAME:{") != 0))
  222.             continue;
  223.         
  224.         theID = theID.substr(4, theID.length-5);
  225.         theName = theName.substr(6, theName.length-7);
  226.         
  227.         var IDRes = RDF.GetResource(theID);
  228.         if (!IDRes) continue;
  229.         
  230.         if (RDFC.IndexOf(IDRes) > 0)
  231.         {
  232.             continue;
  233.         }
  234.         
  235.         // assert the name into the DS
  236.         if (theName)
  237.         {
  238.             var NameLiteral = RDF.GetLiteral(theName);
  239.             datasource.Assert(IDRes, nameRes, NameLiteral, true);
  240.             dirty = true;
  241.         }
  242.  
  243.         // add the element at the appropriate location
  244.         if (isContainerFlag == true)
  245.         {
  246.             RDFC.AppendElement(IDRes);
  247.         }
  248.         else
  249.         {
  250.             RDFC.InsertElementAt(IDRes, pasteNodeIndex++, true);
  251.         }
  252.         dirty = true;
  253.  
  254.             // make sure appropriate bookmark type is set
  255.         var bmTypeNode = datasource.GetTarget( IDRes, typeRes, true );
  256.         if (!bmTypeNode)
  257.         {
  258.             // set default bookmark type
  259.             datasource.Assert(IDRes, typeRes, bmTypeRes, true);
  260.         }
  261.     }
  262.     
  263.     if (dirty)
  264.     {
  265.         var remote = datasource.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource);
  266.         if (remote)
  267.         {
  268.             remote.Flush();
  269.         }
  270.     }
  271.     return true;
  272. }
  273.  
  274. function nsTreeController_delete(tree)
  275. {
  276.     // this should eventually be a parameter to this function
  277.     var promptFlag = false;
  278.         
  279.     if (!tree) return false;
  280.     var select_list = tree.selectedItems;
  281.     if (!select_list) return false;
  282.     if (select_list.length < 1) return false;
  283.     
  284.     var datasource = tree.database;
  285.  
  286.     if (promptFlag == true)
  287.     {
  288.         var deleteStr = '';
  289.         if (select_list.length == 1) {
  290.             deleteStr = get_localized_string("DeleteItem");
  291.         } else {
  292.             deleteStr = get_localized_string("DeleteItems");
  293.         }
  294.         var ok = confirm(deleteStr);
  295.         if (!ok) return false;
  296.     }
  297.  
  298.     var RDFC = Components.classes[rdfc_contractid].getService(nsIRDFContainer);
  299.  
  300.  
  301.     var dirty = false;
  302.  
  303.     // note: backwards delete so that we handle odd deletion cases such as
  304.     //       deleting a child of a folder as well as the folder itself
  305.     for (var nodeIndex=select_list.length-1; nodeIndex>=0; nodeIndex--)
  306.     {
  307.         var node = select_list[nodeIndex];
  308.         if (!node) continue;
  309.         var ID = node.id;
  310.         if (!ID) continue;
  311.  
  312.         // XXX - make tree templates flag special folders as read-only
  313.         // don't allow deletion of various "special" folders
  314.         if ((ID == "NC:BookmarksRoot") || (ID == "NC:IEFavoritesRoot"))
  315.         {
  316.             continue;
  317.         }
  318.  
  319.         var parentID = node.parentNode.parentNode.getAttribute("ref");
  320.         if (!parentID)
  321.             parentID = node.parentNode.parentNode.id;
  322.         if (!parentID) continue;
  323.  
  324.         var RDF = Components.classes[rdf_contractid].getService(Components.interfaces.nsIRDFService);
  325.         var IDRes = RDF.GetResource(ID);
  326.         if (!IDRes) continue;
  327.         var parentIDRes = RDF.GetResource(parentID);
  328.         if (!parentIDRes) continue;
  329.  
  330.         try {
  331.             // first try a container-based approach
  332.             RDFC.Init(datasource, parentIDRes);
  333.             RDFC.RemoveElement(IDRes, true);
  334.         } catch (ex) {
  335.             // nope! just remove the parent/child assertion then
  336.             datasource.Unassert(parentIDRes, containment, IDRes);
  337.  
  338.         }
  339.         dirty = true;
  340.     }
  341.  
  342.     if (dirty == true)
  343.     {
  344.         try {
  345.             var remote = datasource.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource);
  346.             remote.Flush();
  347.         } catch (ex) {
  348.         }
  349.     }
  350.  
  351.     return true;
  352.  
  353. }
  354.  
  355. function nsTreeController(tree)
  356. {
  357.     this.treeId = tree.id;
  358.     tree.controllers.appendController(this);
  359. }
  360.  
  361. nsTreeController.prototype =
  362. {
  363.     // store the tree's Id, rather than the tree,
  364.     // to avoid holding a strong ref
  365.     treeId: null,
  366.     getTree : function()
  367.     {
  368.         return document.getElementById(this.treeId);
  369.     },
  370.     SetTransferData : nsTreeController_SetTransferData,
  371.  
  372.     supportsCommand: function(command)
  373.     {
  374.         switch(command)
  375.         {
  376.             case "cmd_cut":
  377.             case "cmd_copy":
  378.             case "cmd_paste":
  379.             case "cmd_delete":
  380.             case "cmd_selectAll":
  381.                 return true;
  382.             default:
  383.                 return false;
  384.         }
  385.     },
  386.  
  387.     isCommandEnabled: function(command)
  388.     {
  389.         var haveCommand;
  390.         switch (command)
  391.         {
  392.             // commands which do not require selection
  393.             case "cmd_paste":
  394.                 return (this.doPaste != undefined);
  395.                 
  396.             case "cmd_selectAll":
  397.                 // we can always select all
  398.                 return true;
  399.                 
  400.             // these commands require selection
  401.             case "cmd_cut":
  402.                 haveCommand = (this.cut != undefined);
  403.                 break;
  404.             case "cmd_copy":
  405.                 haveCommand = (this.copy != undefined);
  406.                 break;
  407.             case "cmd_delete":
  408.                 haveCommand = (this.doDelete != undefined);
  409.                 break;
  410.         }
  411.         
  412.         // if we get here, then we have a command that requires selection
  413.         var tree = this.getTree();
  414.         var haveSelection = (tree.selectedItems.length > 0);
  415.         return (haveCommand && haveSelection);
  416.     },
  417.     doCommand: function(command)
  418.     {
  419.         switch(command)
  420.         {
  421.             case "cmd_cut":
  422.                 return this.cut(this.getTree());
  423.             
  424.             case "cmd_copy":
  425.                 return this.copy(this.getTree());
  426.             
  427.             case "cmd_paste":
  428.                 return this.paste(this.getTree());
  429.  
  430.             case "cmd_delete":
  431.                 return this.doDelete(this.getTree());
  432.             
  433.             case "cmd_selectAll":
  434.                 return this.getTree().selectAll();
  435.         }
  436.         return false;
  437.     },
  438.     copy: nsTreeController_copy,
  439.     cut: nsTreeController_cut,
  440.     paste: nsTreeController_paste,
  441.     doDelete: nsTreeController_delete
  442. }
  443.  
  444.